import json
from _datetime import datetime
from django.db import DatabaseError
from django.views import View
from django import http
from django.core.cache import cache
from django_redis import get_redis_connection
from django.core.paginator import Paginator
from django.utils.decorators import method_decorator
from django.db import transaction
from django.conf import settings

import logging

from homes.models import Area, House, Facility, HouseImage
from libs.qiniuyun.qiniu_storage import storage
from order.models import Order
from utils import constants
from utils.response_code import RET
from utils.decorators import login_required
from utils.param_checking import image_file


logger = logging.getLogger("django")


class AreaView(View):
    """
    城区列表
    """
    def get(self,request):

        # 读取缓存数据
        areas_list = cache.get("area_info")

        # 查询城区数据
        if not areas_list:
            try:
                areas = Area.objects.all()
            except DatabaseError as e:
                logger.error(e)
                return http.JsonResponse({"errno": RET.DBERR, "errmsg": "数据库查询失败"})

            # 遍历字典数据
            areas_list = [area.to_dict() for area in areas]

            # 缓存省份数据
            cache.set('area_info', areas_list, 3600)

        return http.JsonResponse({"errno": RET.OK, "errmsg": "获取成功", "data": areas_list})



class HousesView(View):
    """
    发布房源
    """

    # 发布房源
    @method_decorator(login_required)
    def post(self,request):
        #接收参数
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')
        price = json_dict.get('price')
        area_id = json_dict.get('area_id')
        address = json_dict.get('address')
        room_count = json_dict.get('room_count')
        acreage = json_dict.get('acreage')
        unit = json_dict.get('unit')
        capacity = json_dict.get('capacity')
        beds = json_dict.get('beds')
        deposit = json_dict.get('deposit')
        min_days = json_dict.get('min_days')
        max_days = json_dict.get('max_days')
        facilitys_id = json_dict.get('facility')

        #验证参数
        if not all([title, price, address, area_id, room_count, acreage, unit, capacity, beds, deposit, min_days,
                    max_days,facilitys_id]):
            return http.JsonResponse({'errno':RET.PARAMERR,'errmsg':'参数错误'})
        # 如果不是int类型抛出异常
        try:
            int(float(price)*100)
            int(float(deposit)*100)
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'errno':RET.PARAMERR,'errmsg':'参数错误'})

        # 查询城区ID
        try:
            area = Area.objects.get(id=area_id)
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({"errno": RET.NODATA, "errmsg": "地址错误"})

        # 开启事务
        with transaction.atomic():
            #设置事务保存点
            sid = transaction.savepoint()
            try:
                house = House()
                house.user = request.user
                house.area = area
                house.title = title
                house.price = price
                house.address = address
                house.room_count = room_count
                house.acreage = acreage
                house.unit = unit
                house.capacity = capacity
                house.beds = beds
                house.deposit = deposit
                house.min_days = min_days
                house.max_days = max_days
                house.save()
            except Exception as e:
                logger.error(e)
                transaction.savepoint_rollback(sid)
                return http.JsonResponse({'errno':RET.DBERR,'errmsg':'保存数据失败'})
            try:
                if facilitys_id:
                    facilitys = Facility.objects.filter(id__in=facilitys_id)
                    for facility in facilitys:
                        # 添加设施信息
                        house.facility.add(facility)
            except Exception as e:
                logger.error(e)
                transaction.savepoint_rollback(sid)
                return http.JsonResponse({'errno':RET.DBERR,'errmsg':'保存设施信息失败'})
            transaction.savepoint_commit(sid)
        # redis_conn  = get_redis_connection('house_cache')
        # redis_conn.flushdb()
        return http.JsonResponse({'errno':RET.OK,'errmsg':'发布成功','data':{'house_id':house.pk}})

    # 房屋数据搜索
    def get(self, request):
        # 获取参数
        area_id = request.GET.get('aid', '')
        start_day = request.GET.get('start_day', '')
        end_day = request.GET.get('ed', '')
        sort_key = request.GET.get('sk', '')
        page = request.GET.get('p', 1)

        # 将分页转为int,若不是int类型抛出异常
        try:
            int(page)
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'errno': RET.PARAMERR, 'errmsg': '参数错误'})
        redis_conn = get_redis_connection('house_cache')
        # 获取Redis缓存数据
        data = redis_conn.hget('house_%s_%s_%s_%s' % (start_day, end_day, sort_key, area_id), page)

        if data:
            return http.JsonResponse({'errno': RET.OK, 'errmsg': '请求成功', 'data': json.loads(data)})
        try:
            sd = None
            ed = None
            if start_day:
                sd = datetime.strptime(start_day, '%Y-%m-%d')
            if end_day:
                ed = datetime.strptime(end_day, '%Y-%m-%d')
            if end_day and start_day:
                # 开始时间若大于结束时间,抛出异常
                try:
                    sd < ed
                except Exception as e:
                    logger.error(e)
                    return http.JsonResponse({"errno": RET.PARAMERR, "errmsg": "参数错误"})
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({"errno": RET.PARAMERR, "errmsg": "参数错误"})

        house_dict = {}
        # 　若区域存在
        if area_id:
            house_dict["area_id"] = area_id
        # 保存冲突的订单
        if sd and ed:
            # 符合order中开始和结束时间才能下订单
            # 订单的开始时间<结束时间 and 订单结束时间 > 开始时间
            orders = Order.objects.filter(begin_date__lt=ed, end_date__gt=sd)
        elif sd:
            # 订单结束时间 > 开始时间
            orders = Order.objects.filter(end_date__gt=sd)
        elif ed:
            # 订单开始时间 < 结束时间
            orders = Order.objects.filter(begin_date__lt=ed)
        else:
            orders = []
        # 获取冲突订单的房屋id
        house_ids = [order.house_id for order in orders]
        if house_ids:
            # 这里的代码参考了终极，因为用filter里直接传area_id = area_id ,id__in=house_ids方法跑不通
            # 学到的经验，模型类查询中，filter中不传参数等于all（）
            house_dict['id__in'] = house_ids
        if sort_key == "booking":
            # 按下的订单量高到低
            house_models = House.objects.filter(**house_dict).order_by("-order_count")
        elif sort_key == "price-inc":
            # 价格高到低
            house_models = House.objects.filter(**house_dict).order_by("price")
        elif sort_key == "price-des":
            # 价格低到高
            house_models = House.objects.filter(**house_dict).order_by("-price")
        else:
            # 默认字段排序，由创建时间由近到远
            house_models = House.objects.filter(**house_dict).order_by("-create_time")
        paginator = Paginator(house_models, constants.HOUSE_LIST_PAGE_CAPACITY)
        page_houses = paginator.page(page)
        total_page = paginator.num_pages
        houses_list = [page_house.to_basic_dict() for page_house in page_houses]
        data = {
            "total_page": total_page,
            "houses": houses_list
        }
        try:
            # 缓存数据
            redis_conn.hset("houses_%s_%s_%s_%s" % (start_day, end_day, sort_key, area_id), page, json.dumps(data))
            # expire:给缓存添加有效期
            redis_conn.expire("houses_%s_%s_%s_%s" % (start_day, end_day, sort_key, area_id),
                              constants.HOUSE_LIST_REDIS_EXPIRES)
        except Exception as e:
            logger.error(e)
        return http.JsonResponse({'errno': RET.OK, 'errmsg': '请求成功', 'data': data})

class UserHouseView(View):
    """
    我的房屋列表
    """
    @method_decorator(login_required)
    def get(self, request):
        # 获取用户并查询
        user = request.user
        houses = [house.to_basic_dict() for house in user.houses.all()]

        return http.JsonResponse({"errno": RET.OK, "errmsg": "成功", "data": {'houses': houses}})


class HouseImageView(View):
    """
    房源图片上传
    """
    def post(self,request,house_id):

        # 接收参数
        house_image = request.FILES.get("house_image")
        # 判断是否有参数
        if not house_image:
            return http.JsonResponse({"errno": RET.PARAMERR, "errmsg": "参数错误"})
        #　判断是否是图片文件
        if not image_file(house_image):
            return http.JsonResponse({"errno": RET.PARAMERR, "errmsg": "参数错误"})

        # 查询数据
        try:
            house = House.objects.get(id=house_id)
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({"errno": RET.NODATA, "errmsg": "该房间不存在"})

        # 读取文件对象二进制数据
        house_image_data = house_image.read()

        try:
            # 利用七牛云上传图片文件
            key = storage(house_image_data)
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({"errno": RET.THIRDERR, "errmsg": "上传图片失败"})
        # 开启事务
        with transaction.atomic():
            # 创建事务回滚点
            save_id = transaction.savepoint()
            try:
                if not house.index_image_url:
                    house.index_image_url = key
                    house.save()

                house_image = HouseImage()
                house_image.house = house
                house_image.url = key
                house_image.save()
            except Exception as e:
                logger.error(e)
                # 事务回滚
                transaction.savepoint_rollback(save_id)
            # 提交事务
            transaction.savepoint_commit(save_id)

        data = {
            "url": settings.QINIU_URL + key
        }
        return http.JsonResponse({"errno": RET.OK, "errmsg": "OK", "data": data})


class HouseIndexView(View):
    """
    首页房屋推荐
    """
    def get(self,request):

        # house_list = cache.get("house_index")
        # if not house_list:
        # 查询订单数量前五的数据
        try:
            houses = House.objects.order_by("-order_count")[0:5]
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({"errno": RET.DBERR, "errmsg": "数据库查询失败"})

        houses_list = [house.to_basic_dict() for house in houses]
        cache.set('house_index', houses_list, 3600)

        # 返回数据
        return http.JsonResponse({"errno": RET.OK, "errmsg": "OK", "data": houses_list})


class HouseDetail(View):
    """
    房屋详情页
    """
    def get(self,request,house_id):
        # 查询房屋信息数据
        try:
            house = House.objects.get(id=house_id)
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({"errno": RET.PARAMERR, "errmsg": "参数错误"})

        # 获取user
        user = request.user
        # 判断是否是登陆用户
        if not user.is_authenticated:
            user_id = -1
        else:
            user_id = user.id
        # 从redis中读取缓存
        redis_conn = get_redis_connection("house_cache")
        house_dict = redis_conn.get("house_info" + house_id)
        # 如果有返回数据
        if house_dict:
            return http.JsonResponse({"errno": RET.OK, "errmsg": "OK", "data":{"user_id": user_id, "house": json.loads(house_dict)}})

        # 如果没有,数据缓存到redis中
        house_dict = house.to_full_dict()
        try:
            redis_conn.setex('house_info_' + house_id, constants.HOUSE_DETAIL_REDIS_EXPIRE_SECOND,
                             json.dumps(house_dict))
        except Exception as e:
            logger.error(e)
        # 返回数据
        return http.JsonResponse({"errno": RET.OK, "errmsg": "OK", "data": {"user_id": user_id, "house": house_dict}})